
#ifndef CACHE_H
#define CACHE_H

// INCLUDES ********************************************************************

#include <string>

#include <map>

#include <set>

#include "Logger.h"

#include "Function.h"

// FORWARD DECLARATIONS ********************************************************

// *****************************************************************************

//! Cache
//! 
//! Class template for a cache-like storing structure, usage prevents doubled
//! objects. if an object for the key provided already exists, Cache will return
//! that object instead. 

// *****************************************************************************

namespace Meteor
{
    template <typename T>
    class Cache
    {
        public:
            Cache();
        
            virtual ~Cache();
            
            //! adding the object passed for the corresponding key
            //! the cache will take ownership for this object
            virtual T AddObject( const std::string& key, T object);
            
            virtual void AddObjectAsync( const std::string& fileName, Function* fileAdded=0)=0;
            
            //! remove an object from the cache
            virtual void RemoveObject( const std::string& key);
        
            //! find an object, if existant
            virtual T* FindObject( const std::string& key);
            
            //! called after an object has been added asynchronously
            void ObjectAdded( std::string filename);
            
            //! empty cache, removing all fonts
            void Flush();
            
        protected:
            typedef std::map<std::string, std::pair<T, int> > containerType;
        
            //! associative container holding templated types together
            //! with a reference count
            containerType mObjects;
            
            uint32_t mAsyncFileCounter;

            typedef std::map<std::string, Function*> asyncContainerType;
            
            asyncContainerType mAsyncFiles;
        
        private:
            //! prevent copies
            Cache( const Cache&);
            
            const Cache& operator=( const Cache&);

    }; // end of class Cache

} // end of namespace Meteor


#pragma mark -
#pragma mark Constructor/Destructor
// -----------------------------------------------------------------------------
template <typename T>
Meteor::Cache<T>::Cache()
{
}

// -----------------------------------------------------------------------------

template <typename T>
Meteor::Cache<T>::~Cache()
{
    Flush();
}


#pragma mark -
#pragma mark Add/remove objects
// -----------------------------------------------------------------------------

template <typename T>
T Meteor::Cache<T>::AddObject( const std::string& key, T object)
{
    typename containerType::iterator iter = mObjects.find( key);
    
    if ( iter != mObjects.end())
    {
        //! increase reference count
        iter->second.second++;
        
        delete object;
        
        return iter->second.first;
    }
    
    std::pair<T, int> newCacheObject( object, 1);
    
    mObjects.insert( std::pair<std::string, std::pair<T, int> >( key, newCacheObject));
    
    return object;
}

// -----------------------------------------------------------------------------

template <typename T>
void Meteor::Cache<T>::RemoveObject( const std::string& key)
{
    typename containerType::iterator iter = mObjects.find( key);
    
    if ( iter == mObjects.end())
    {
        return;
    }
    
    //! reduce reference count
    iter->second.second--;
    
    //! there still is a reference to the object, so no deletion
    if ( iter->second.second > 0)
    {
        return;
    }
    
    delete &iter->second.first;
    
    mObjects.erase( iter);
}

// -----------------------------------------------------------------------------

template <typename T>
T* Meteor::Cache<T>::FindObject( const std::string& key)
{
    typename containerType::iterator entry = mObjects.find( key);
    
    if ( entry == mObjects.end())
    {
        return 0;
    }
    else
    {
        return &entry->second.first;
    }
}


#pragma mark -
#pragma mark Empty cache
// -----------------------------------------------------------------------------

template <typename T>
void Meteor::Cache<T>::Flush()
{
    typename containerType::iterator iterator;
    
    iterator = mObjects.begin();
    
    for ( uint32_t count = 0; count < mObjects.size(); count++, iterator++)
    {
        delete iterator->second.first;
    }
    
    mObjects.clear();
    
    mAsyncFiles.clear();
}

#pragma mark -
#pragma mark Asynchronous adding needs to tell when done
// -----------------------------------------------------------------------------

template <typename T>
void Meteor::Cache<T>::ObjectAdded( std::string filename)
{
    asyncContainerType::iterator object = mAsyncFiles.find( filename);

    Function* fileAdded = ( *object).second;
    
    if ( fileAdded != 0)
    {
        ( *fileAdded)();
    }
    
    delete fileAdded;
    
    mAsyncFiles.erase( object);
}

#endif

